#include <stddef.h>
#include <stdlib.h>
#include <string.h>
#include "n2base.h"
#include "StringPool.h"

N2StringPool::N2StringPool()
{
    alloc_size = 0;
    curptr = 0;
    base = 0;
    pool = NULL;

    base = 1024;
    pool = new char[base];
    if (pool == NULL)
        throw N2Excep_NoMemory;
    alloc_size = base-16;
}

N2StringPool::N2StringPool(size_t incbase)
{
    alloc_size = 0;
    curptr = 0;
    base = 0;
    pool = NULL;

    if (incbase > 1024)
        base = incbase;
    else
        base = 1024;
    pool = new char[base];
    if (pool == NULL)
        throw N2Excep_NoMemory;
    alloc_size = base-16;
}

N2StringPool::~N2StringPool()
{
    clearAndFreePool();
}

size_t N2StringPool::addStr(const char* str)
{
    if (str == NULL)
        return 0;
    return addByteData(str, strlen(str)+1, 1);
}

size_t N2StringPool::addUtf16Str(const unsigned short* str)
{
    size_t len;
    if (str == NULL)
        return 0;
    len = 0;
    while (str[len])
        len++;
    return addByteData((const char*)str, (len+1)*2, 2);
}

size_t N2StringPool::addUtf32Str(const unsigned int* str)
{
    size_t len;
    if (str == NULL)
        return 0;
    len = 0;
    while (str[len])
        len++;
    return addByteData((const char*)str, (len+1)*4, 4);
}

const char * N2StringPool::getStr(size_t offset)
{
    return &pool[offset];
}

const unsigned short * N2StringPool::getUtf16Str(size_t offset)
{
    return (const unsigned short*) (&pool[offset]);
}

const unsigned int * N2StringPool::getUtf32Str(size_t offset)
{
    return (const unsigned int*) (&pool[offset]);
}

size_t N2StringPool::addByteData(const char* str, size_t length, int align)
{
    size_t add;
    size_t i;
    size_t offset_backup;
    if (pool == NULL)
        return 0;
    if (align > 1) {
        if (curptr % align) {
            add = align - (curptr % align);
            curptr += add;
        }
    }

    if (curptr + length >= alloc_size)
        incPoolSize();

    offset_backup = curptr;
    for(i = 0; i < length; i++)
        pool[curptr+i] = str[i];
    curptr += length;
    return offset_backup;
}

void N2StringPool::incPoolSize()
{
    size_t i;
    char * new_pool;

    n2print("IncPoolSize\n");
    alloc_size += base;
    new_pool = new char[alloc_size+16];
    if (new_pool == NULL)
        throw N2Excep_NoMemory;
    for (i = 0; i < curptr; i++)
        new_pool[i] = pool[i];
    delete[] pool;

    pool = new_pool;
    return;
}

size_t N2StringPool::getPoolSize()
{
    return alloc_size + 16;
}

size_t N2StringPool::getUsedSize()
{
    return curptr;
}

void N2StringPool::clearPool()
{
    curptr = 0;
}

void N2StringPool::clearAndFreePool()
{
    curptr = 0;
    delete[] pool;
    pool = NULL;
    alloc_size = 0;
}

//=================================================================
N2StringPool * N2PoolString::pool = NULL;

N2PoolString::N2PoolString()
{
    length = 0;
    data.str[0] = 0;
}

N2PoolString::N2PoolString(const char* str)
{
    setstr(str);
}

void N2PoolString::setStringPool(N2StringPool * stringpool)
{
    pool = stringpool;
}

size_t N2PoolString::getLength()
{
    return length;
}

const char * N2PoolString::c_str() const
{
    if (length >= 8)
    {
        if (pool)
            return pool->getStr(data.offset);
        else
            return "";
    }
    else
        return data.str;
}

const char * N2PoolString::setstr(const char* str)
{
    if (str == NULL)
    {
        length = 0;
        data.str[0] = 0;
        return data.str;
    }

    length = strlen(str);
    if (length < 8)
    {
        strncpy(data.str, str, 7);
        data.str[7] = 0;
    }
    else
    {
        if (pool)
        {
            data.offset = pool->addStr(str);
        }
        else
        {
            length = 0;
            data.str[0] = 0;
            return data.str;
        }
    }
    return str;
}

//=================================================================

N2DataPool::N2DataPool(size_t minSize, size_t blockbase)
{
    memset(this, 0, sizeof(N2DataPool));
    minBlockSize = minSize;
    block_base = blockbase;
}

N2DataPool::~N2DataPool()
{
    clearAllData();
    if (blocks) {
        delete[] blocks;
        blocks = NULL;
    }
    curblockid = 0;
    block_alloc = 0;
}

void N2DataPool::increaseBlockNumber()
{
    size_t i;
    if (blocks)
    {
        size_t newAlloc;
        TN2DataPoolBlock * oldblocks;
        newAlloc = block_alloc + block_base;
        oldblocks = blocks;
        blocks = new TN2DataPoolBlock[newAlloc];
        if (blocks == NULL)
            throw N2Excep_NoMemory;
        for (i = 0; i < block_alloc; i++)
        {
            blocks[i].data = oldblocks[i].data;
            blocks[i].blocksize = oldblocks[i].blocksize;
            blocks[i].used = oldblocks[i].used;
            oldblocks[i].data = NULL;
            oldblocks[i].blocksize = 0;
            oldblocks[i].used = 0;
        }
        for (; i < newAlloc; i++)
        {
            blocks[i].data = NULL;
            blocks[i].blocksize = 0;
            blocks[i].used = 0;
        }
        block_alloc = newAlloc;
        delete[] oldblocks;
    }
    else
    {
        blocks = new TN2DataPoolBlock[block_base];
        if (blocks == NULL)
            throw N2Excep_NoMemory;
        block_alloc = block_base;
        for (i = 0; i < block_alloc; i++) {
            blocks[i].data = NULL;
            blocks[i].blocksize = 0;
            blocks[i].used = 0;
        }
    }
}

void N2DataPool::addBinaryData(unsigned char * data, size_t size)
{
    size_t i;
    TN2DataPoolBlock * curblock;

    if (blocks == NULL)
        increaseBlockNumber();
    curblock = &blocks[curblockid];
    if (curblock->data == NULL)
    {
        curblock->data = new unsigned char[minBlockSize];
        if (curblock->data == NULL)
            throw N2Excep_NoMemory;
        curblock->used = 0;
        curblock->blocksize = minBlockSize;
    }

    if (curblock->used + size >= curblock->blocksize)
    {
        size_t  part1_size, part2_size;
        part1_size = curblock->blocksize - curblock->used;
        part2_size = size - part1_size;
        for (i = 0; i < part1_size; i++)
            curblock->data[curblock->used + i] = data[i];
        curblock->used += part1_size;

        curblockid++;
        if (curblockid >= block_alloc)
            increaseBlockNumber();

        curblock = &blocks[curblockid];
        if (curblock->data == NULL)
        {
            size_t newAllocSize;
            if (minBlockSize > part2_size)
                newAllocSize = minBlockSize;
            else
                newAllocSize = part2_size;

            curblock->data = new unsigned char[newAllocSize];
            if (curblock->data == NULL)
                throw N2Excep_NoMemory;
            curblock->used = 0;
            curblock->blocksize = newAllocSize;
        }
        for (i = 0; i < part2_size; i++)
            curblock->data[curblock->used + i] = data[part1_size+i];
        curblock->used += part2_size;
    }
    else
    {
        for (i = 0; i < size; i++)
            curblock->data[curblock->used + i] = data[i];
        curblock->used += size;
    }
    datasize += size;
}

n2u64 N2DataPool::getTotalDataSize()
{
    return datasize;
}

void N2DataPool::clearAllData()
{
    size_t i;
    if (blocks == NULL)
        return;
    for (i = 0; i < block_alloc; i++)
    {
        if (blocks[i].data) {
            delete[] blocks[i].data;
            blocks[i].data = NULL;
            blocks[i].used = 0;
            blocks[i].blocksize = 0;
        }
    }
    curblockid = 0;
    datasize = 0;
}






